package com.szlanyou.thirdsdk.retrofit.client;

import android.app.Activity;
import android.content.Context;

import com.szlanyou.thirdsdk.retrofit.callback.FileUploadProgressHttpResultHandlerHolder;
import com.szlanyou.thirdsdk.retrofit.callback.IFileUploadProgressHttpResultHandler;
import com.szlanyou.thirdsdk.retrofit.callback.IHttpResultHandler;
import com.szlanyou.thirdsdk.retrofit.convert.AbsResponseBodyHttpResultHandler;
import com.szlanyou.thirdsdk.retrofit.file.download.IHttpDownloadProgressResultHandler;
import com.szlanyou.thirdsdk.retrofit.file.upload.FileUploadObservableGenerator;
import com.szlanyou.thirdsdk.retrofit.file.upload.UploadFileWrapper;
import com.szlanyou.thirdsdk.retrofit.loading.INetLoadingManage;
import com.szlanyou.thirdsdk.retrofit.loading.NetLoadingStateAction;
import com.szlanyou.thirdsdk.retrofit.util.MyLogUtil;
import com.szlanyou.thirdsdk.retrofit.util.RequestBodyUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import io.reactivex.Observable;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;

/**
 * @author 陈章
 * create at 2020/12/22 9:03
 * desc:对HttpMaster的二次封装，去掉Retrofit元素,让用户在不熟悉Retrofit的情况下也能使用。
 * <p>
 * 没有做成单例，因为一个应用可能要与多个不同厂家的接口对接，base url及返回格式可能都不一样。
 * <p>
 * <p>
 * 使用说明：
 * 1.创建实例
 * 2.初始化{@link #init(Context, boolean...)}（传入context，设置日志开关）
 * 3.设置Base Url {@link #setBaseUrl(String)}
 * 4.调用{@link #createResponseBodyHttpResultHandler(IHttpResultHandler)}将json转换为具体的bean
 */
public abstract class HttpClient implements IHttpClient, INetLoadingManage {
    private static final String TAG = "HttpClient";
    private HttpMaster httpMaster;
    private HttpClientService httpClientService;
    private RetrofitClient retrofitClient;
    private ClientCommonParamsManager clientCommonParamsManager;
    private LoadingActionManager loadingActionManager;

    /**
     * 用于处理业务的响应实体类。
     */
    private List<AbsResponseBodyHttpResultHandler> responseBodyHttpResultHandlers;

    /**
     * HttpClient默认的Loading配置
     */
    private LoadingConfig loadingConfig;

    //构造空Map，用于传参，防止retrofit传递参数为null报错。
    private final Map<String, Object> EMPTY_MAP = new LinkedHashMap<>(0);


    @Override
    public void init(Context mContext, boolean... openLog) {
        this.httpMaster = new HttpMaster(mContext, this);
        this.retrofitClient = new RetrofitClient(this);
        this.clientCommonParamsManager = new ClientCommonParamsManager();
        this.loadingActionManager = new LoadingActionManager();
        this.loadingConfig = new LoadingConfig();
        this.responseBodyHttpResultHandlers = new ArrayList<>();

        if (openLog != null && openLog.length == 1) {
            MyLogUtil.setLogOpen(openLog[0]);
        } else {
            MyLogUtil.setLogOpen(false);
        }
    }

    @Override
    public void setBaseUrl(String baseUrl) {
        retrofitClient.setBaseUrl(baseUrl);

        //下面的service一定要在setBaseUrl之后生成，否则请求时url不会发生变化。
        httpClientService = retrofitClient.getService(HttpClientService.class);
    }

    /**
     * 注册响应体处理器（一般用于组件化，不同的业务模块解析自己的响应实体类。）
     *
     * @param absResponseBodyHttpResultHandler
     */
    public <T extends AbsResponseBodyHttpResultHandler> void registerResponseBodyHttpResultHandler(T absResponseBodyHttpResultHandler) {
        responseBodyHttpResultHandlers.add(absResponseBodyHttpResultHandler);
    }

    @Override
    public <T> T getService(Class<T> serviceClass) {
        return retrofitClient.getService(serviceClass);
    }

    @Override
    public void setOnRequestInterceptor(HttpMaster.OnRequestInterceptor onRequestInterceptor) {
        httpMaster.setOnRequestInterceptor(onRequestInterceptor);
    }

    @Override
    public void setAutoShowLoading(boolean autoShowLoading) {
        this.loadingConfig.autoShowLoading = autoShowLoading;
    }

    @Override
    public void setAutoShowLoading(boolean autoShowLoading, String loadingTipTxt) {
        this.loadingConfig.autoShowLoading = autoShowLoading;
        this.loadingConfig.loadingTipTxt = loadingTipTxt;
    }

    @Override
    public void setAutoDismissLoading(boolean autoDismissLoading) {
        this.loadingConfig.autoDismissLoading = autoDismissLoading;
    }

    @Override
    public void dismissLoading() {
        httpMaster.dismissLoading();
    }

    @Override
    public void cancel(String requestCodeOut) {
        httpMaster.cancel(requestCodeOut);
    }

    @Override
    public void cancel(Set<String> requestCodeSet) {
        httpMaster.cancel(requestCodeSet);
    }

    @Override
    public void cancelByPageFlag(String requestPageFlag) {
        httpMaster.cancelByPageFlag(requestPageFlag);
    }

    @Override
    public void cancelAll() {
        httpMaster.cancelAll();
    }

    @Override
    public void addCommonParam(String key, Object value) {
        clientCommonParamsManager.addCommonParam(key, value);
    }

    @Override
    public void removeCommonParam(String key) {
        clientCommonParamsManager.removeCommonParam(key);
    }

    @Override
    public void addCommonHeader(String key, Object value) {
        clientCommonParamsManager.addCommonHeader(key, value);
    }

    @Override
    public void removeCommonHeader(String key) {
        clientCommonParamsManager.removeCommonHeader(key);
    }

    @Override
    public void setOnRequestBodyInterceptListener(ClientCommonParamsManager.OnRequestBodyInterceptListener onRequestBodyInterceptListener) {
        clientCommonParamsManager.setOnRequestBodyInterceptListener(onRequestBodyInterceptListener);
    }

    @Override
    public void setLoadingAction(NetLoadingStateAction loadingAction) {
        loadingActionManager.setLoadingAction(loadingAction);
    }


    @Override
    public NetLoadingStateAction getLoadingAction() {
        return loadingActionManager.getLoadingAction();
    }

    @Override
    public void releaseLoadingAction(Activity activity) {
        loadingActionManager.releaseLoadingAction(activity);
    }

    /**
     * 处理ResponseBody
     * 将ResonseBody转化成Object
     * 注意这里的Object可能是
     * {
     * code:xx,
     * msg:xx,
     * data:{}
     * }
     * 也可能是data
     * 取决于IHttpResultHandler的泛型类型
     *
     * @param httpResultHandler
     * @return
     */
    public AbsResponseBodyHttpResultHandler createResponseBodyHttpResultHandler(IHttpResultHandler httpResultHandler) {
        return new AbsResponseBodyHttpResultHandler(httpResultHandler) {
            @Override
            public void onReceiveJson(String requestPageFlag, String requestCode, String json, IHttpResultHandler httpResultHandler, Class httpResultHandlerClass) {
                //让HttpClient的默认AbsResponseBodyHttpResultHandler拦截处理
                AbsResponseBodyHttpResultHandler defaultResponseBodyHttpResultHandler = createDefaultResponseBodyHttpResultHandler(httpResultHandler);
                if (defaultResponseBodyHttpResultHandler != null) {
                    defaultResponseBodyHttpResultHandler.attach(httpResultHandler);
                    defaultResponseBodyHttpResultHandler.onReceiveJson(requestPageFlag, requestCode, json, httpResultHandler, httpResultHandlerClass);
                }

                //让组件化的responseBodyHttpResultHandlers拦截处理
                for(AbsResponseBodyHttpResultHandler item : responseBodyHttpResultHandlers){
                    item.attach(httpResultHandler);
                    item.onReceiveJson(requestPageFlag, requestCode, json, httpResultHandler, httpResultHandlerClass);
                }
            }
        };

    }

    /**
     * 为{@link HttpClient}生成一个默认的响应体处理器.
     *
     * @param httpResultHandler
     * @return
     */
    public abstract AbsResponseBodyHttpResultHandler createDefaultResponseBodyHttpResultHandler(IHttpResultHandler httpResultHandler);

    public RetrofitClient getRetrofitClient() {
        return retrofitClient;
    }

    /**
     * 创建网络请求的发起者
     *
     * @return
     */
    public RequestBuilder newRequestBuilder() {
        return new RequestBuilder();
    }


    /**
     * 构建网络请求的发起者
     *
     * @param <K>
     */
    public class RequestBuilder<K> {
        private static final String TAG = "RequestBuilder";
        //## 一、公共参数
        //1.请求类型
        private @RequestType
        String type;

        //2.请求地址
        private String url;

        //3.是否显示loading
        private boolean autoShowLoading;

        //4.loading显示的文字
        private String loadingTipTxt;

        //5.loading是否自动消失（测试之用）
        private boolean autoDismissLoading;

        //6.请求码
        private String requestCodeOut;

        //7.请求参数
        private Map<String, Object> paramsMap;

        //8.头参数
        private Map<String, Object> headerMap;

        //请求回调
        private IHttpResultHandler<K> httpResultHandler;

        //## 二、文件上传
        private List<UploadFileWrapper> uploadFileWrapperList;

        //## 三、文件下载
        private File outputDirOrFile;


        private RequestBuilder() {
            this.type = null;
            this.url = null;
            this.autoShowLoading = loadingConfig.autoShowLoading;
            this.autoDismissLoading = loadingConfig.autoDismissLoading;
            this.loadingTipTxt = loadingConfig.loadingTipTxt;
            this.httpResultHandler = null;
            this.requestCodeOut = null;
            this.paramsMap = null;
            this.headerMap = null;
            this.uploadFileWrapperList = null;
            this.outputDirOrFile = null;
        }


        /**
         * 设置请求类型（必选）
         *
         * @param type
         * @return
         */
        public RequestBuilder type(@RequestType String type) {
            this.type = type;
            return this;
        }

        /**
         * 设置请求地址(必选)
         *
         * @param url
         * @return
         */
        public RequestBuilder url(String url) {
            this.url = url;
            return this;
        }

        /**
         * 设置是否显示loading框 （可选）
         * 默认 true
         *
         * @param autoShowLoading
         * @return
         */
        public RequestBuilder autoShowLoading(boolean autoShowLoading) {
            this.autoShowLoading = autoShowLoading;
            return this;
        }

        /**
         * 设置loading框显示的文字（可选）
         *
         * @param loadingTipTxt
         * @return
         */
        public RequestBuilder loadingTipTxt(String loadingTipTxt) {
            this.loadingTipTxt = loadingTipTxt;
            return this;
        }

        /**
         * 设置loading框是否自动消失（可选）
         *
         * @param autoDismissLoading
         * @return
         */
        public RequestBuilder autoDismissLoading(boolean autoDismissLoading) {
            this.autoDismissLoading = autoDismissLoading;
            return this;
        }

        /**
         * 请求的结果回调（可选）
         *
         * @param httpResultHandler
         * @return
         */
        public RequestBuilder httpResultHandler(IHttpResultHandler<K> httpResultHandler) {
            this.httpResultHandler = httpResultHandler;
            return this;
        }

        /**
         * 请求码，标识请求，用于取消网络请求。(可选）
         *
         * @param requestCodeOut
         * @return
         */
        public RequestBuilder requestCode(String requestCodeOut) {
            this.requestCodeOut = requestCodeOut;
            return this;
        }

        /**
         * 添加单个请求参数(可选）
         *
         * @param key   键
         * @param value 值
         * @return
         */
        public RequestBuilder addParam(String key, Object value) {
            if (paramsMap == null) {
                paramsMap = new LinkedHashMap<>();
            }
            paramsMap.put(key, value);
            return this;
        }

        /**
         * 添加单个请求头(可选）
         *
         * @param key   键
         * @param value 值
         * @return
         */
        public RequestBuilder addHeader(String key, Object value) {
            if (headerMap == null) {
                headerMap = new LinkedHashMap<>();
            }
            headerMap.put(key, value);
            return this;
        }

        /**
         * 设置请求参数列表(可选）
         * 会覆盖{@link #addParam(String, Object)}
         *
         * @param paramsMap 请求参数列表
         * @return
         */
        public RequestBuilder setParams(Map<String, Object> paramsMap) {
            this.paramsMap = paramsMap;
            return this;
        }

        /**
         * 设置请求头参数列表(可选）
         * 会覆盖{@link #addHeader(String, Object)}
         *
         * @param paramsMap 请求头列表
         * @return
         */
        public RequestBuilder setHeaders(Map<String, Object> paramsMap) {
            this.headerMap = paramsMap;
            return this;
        }

        /**
         * 设置单个上传文件(可选）
         *
         * @param uploadFile 上传文件
         * @return
         */
        public RequestBuilder setUploadFile(UploadFileWrapper uploadFile) {
            if (this.uploadFileWrapperList == null) {
                this.uploadFileWrapperList = new ArrayList<>();
            }
            uploadFileWrapperList.clear();
            uploadFileWrapperList.add(uploadFile);
            return this;
        }

        /**
         * 设置上传文件列表(可选）
         * 会覆盖{@link #setUploadFile(UploadFileWrapper)}
         *
         * @param uploadFileList 上传文件列表
         * @return
         */
        public RequestBuilder setUploadFile(List<UploadFileWrapper> uploadFileList) {
            this.uploadFileWrapperList = uploadFileList;
            return this;
        }


        /**
         * 设置下载文件的保存路径或者文件名
         * 保存路径为文件夹，则自动根据时间戳在目录下生成文件名;保存路径为文件名，则保存文件为文件名。
         *
         * @param outputDirOrFile 路径或者文件名
         * @return
         */
        public RequestBuilder setDownloadFileOutputDirOrFileName(File outputDirOrFile) {
            this.outputDirOrFile = outputDirOrFile;
            return this;
        }

        /**
         * 发起请求
         */
        public void request() {
            this.request(null);
        }

        /**
         * 发起请求
         */
        public void request(String requestPageFlag) {
            //判断必要参数的传入情况
            if (type == null) {
                MyLogUtil.e(TAG, "please call type() to config request method, use GET 、POST、FILE_UPLOAD 、FILE_DOWNLOAD");
                return;
            }

            if (url == null) {
                MyLogUtil.e(TAG, "please call url() to config request url");
                return;
            }

            switch (type) {
                case RequestType.GET:
                    new GetRequestExecutor(this).request(requestPageFlag);
                    break;
                case RequestType.POST:
                    new PostRequestExecutor(this).request(requestPageFlag);
                    break;
                case RequestType.FILE_UPLOAD:
                    new FileUploadRequestExecutor(this).request(requestPageFlag);
                    break;
                case RequestType.FILE_DOWNLOAD:
                    new FileDownloadRequestExecutor(this).request(requestPageFlag);
                    break;
            }
        }
    }

    /**
     * GET/POST网络请求发起基类
     */
    private abstract class BaseRequestExecutor {
        protected RequestBuilder requestBuilder;
        protected String requestPageFlag;

        private BaseRequestExecutor(RequestBuilder requestBuilder) {
            this.requestBuilder = requestBuilder;
            this.requestPageFlag = null;
        }

        /**
         * 发起请求
         */
        protected abstract void request();

        /**
         * 发起请求(带页面标识)
         */
        protected abstract void request(String requestPageFlag);

        /**
         * 将私有参数与公共参数拼接
         *
         * @return
         */
        protected Map<String, Object> getParamsMap() {
            Map<String, Object> paramsMap = requestBuilder.paramsMap == null ? EMPTY_MAP : requestBuilder.paramsMap;
            //不能直接用paramsMap去拼接，否则会乱序。
            LinkedHashMap<String, Object> paramsMapProxy = new LinkedHashMap<>();
            paramsMapProxy.putAll(paramsMap);

            Map<String, Object> commonParams = clientCommonParamsManager.getCommonParams();
            if (!commonParams.isEmpty()) {
                paramsMapProxy.putAll(commonParams);
            }
            return paramsMapProxy;
        }

        /**
         * 将私有请求头与公共请求头拼接
         *
         * @return
         */
        protected Map<String, Object> getHeaderMap() {
            Map<String, Object> headerMap = requestBuilder.headerMap == null ? EMPTY_MAP : requestBuilder.headerMap;
            //不能直接用paramsMap去拼接，否则会乱序。
            LinkedHashMap<String, Object> headerMapProxy = new LinkedHashMap<>();
            headerMapProxy.putAll(headerMap);

            Map<String, Object> commonHeaders = clientCommonParamsManager.getCommonHeaders();
            if (!commonHeaders.isEmpty()) {
                headerMapProxy.putAll(commonHeaders);
            }
            return headerMapProxy;
        }
    }

    /**
     * GET请求执行
     */
    private class GetRequestExecutor extends BaseRequestExecutor {
        private GetRequestExecutor(RequestBuilder requestBuilder) {
            super(requestBuilder);
        }

        @Override
        protected void request() {
            this.request(null);
        }

        @Override
        protected void request(String requestPageFlag) {
            Observable<ResponseBody> get;
            Map<String, Object> paramsMap = getParamsMap();
            Map<String, Object> headerMap = getHeaderMap();
            get = httpClientService.get(requestBuilder.url, paramsMap, headerMap);
            httpMaster.request(get, requestBuilder.autoShowLoading, requestBuilder.autoDismissLoading, requestBuilder.loadingTipTxt, createResponseBodyHttpResultHandler(requestBuilder.httpResultHandler), requestPageFlag, requestBuilder.requestCodeOut);
        }
    }

    /**
     * POST请求执行
     */
    private class PostRequestExecutor extends BaseRequestExecutor {
        public PostRequestExecutor(RequestBuilder requestBuilder) {
            super(requestBuilder);
        }

        @Override
        protected void request() {
            this.request(null);
        }

        @Override
        protected void request(String requestPageFlag) {
            Map<String, Object> paramsMap = getParamsMap();
            Map<String, Object> headerMap = getHeaderMap();
            RequestBody requestBodyFromMap = RequestBodyUtil.createRequestBodyFromMap(paramsMap);
            Observable<ResponseBody> post = httpClientService.post(requestBuilder.url, requestBodyFromMap, headerMap);
            httpMaster.request(post, requestBuilder.autoShowLoading, requestBuilder.autoDismissLoading, requestBuilder.loadingTipTxt, createResponseBodyHttpResultHandler(requestBuilder.httpResultHandler), requestPageFlag, requestBuilder.requestCodeOut);
        }
    }

    /**
     * 文件上传请求执行
     */
    private class FileUploadRequestExecutor extends BaseRequestExecutor {
        private FileUploadRequestExecutor(RequestBuilder requestBuilder) {
            super(requestBuilder);
        }

        @Override
        protected void request() {
            this.request(null);
        }

        @Override
        protected void request(String requestPageFlag) {
            //判断必要参数的传入情况
            if (requestBuilder.uploadFileWrapperList == null || requestBuilder.uploadFileWrapperList.isEmpty()) {
                MyLogUtil.e(TAG, "please call setUploadFile() to config upload file");
                return;
            }

            AbsResponseBodyHttpResultHandler httpResultHandlerWrapper = createResponseBodyHttpResultHandler(requestBuilder.httpResultHandler);

            //缓存，用于拦截器里回调上传进度。
            IHttpResultHandler originalHttpResultHandler = httpResultHandlerWrapper.getOriginalHttpResultHandler();
            if (originalHttpResultHandler instanceof IFileUploadProgressHttpResultHandler) {
                FileUploadProgressHttpResultHandlerHolder.add(originalHttpResultHandler.getUniqueRequestId(), (IFileUploadProgressHttpResultHandler) originalHttpResultHandler);
            }
            Map<String, Object> paramsMap = getParamsMap();
            Map<String, Object> headerMap = getHeaderMap();
            Observable<ResponseBody> upload = FileUploadObservableGenerator.create(HttpClient.this, requestBuilder.url, paramsMap, requestBuilder.uploadFileWrapperList, originalHttpResultHandler.getUniqueRequestId(), headerMap);
            httpMaster.request(upload, requestBuilder.autoShowLoading, requestBuilder.autoDismissLoading, requestBuilder.loadingTipTxt, httpResultHandlerWrapper, requestPageFlag, requestBuilder.requestCodeOut);
        }
    }


    /**
     * 文件下载请求执行
     */
    private class FileDownloadRequestExecutor extends BaseRequestExecutor {
        private FileDownloadRequestExecutor(RequestBuilder requestBuilder) {
            super(requestBuilder);
        }

        @Override
        protected void request() {
            this.request(null);
        }

        @Override
        protected void request(String requestPageFlag) {
            if (requestBuilder.outputDirOrFile == null) {
                MyLogUtil.e(TAG, "please call setDownloadFileOutputDirOrFileName() to config download file save path or file name");
                return;
            }

            if (requestBuilder.httpResultHandler != null && !(requestBuilder.httpResultHandler instanceof IHttpDownloadProgressResultHandler)) {
                MyLogUtil.e(TAG, "download file,the callback must extend IHttpDownloadProgressResultHandler ");
                return;
            }
            httpMaster.downloadFile(requestBuilder.autoShowLoading, requestBuilder.autoDismissLoading, requestBuilder.loadingTipTxt, requestBuilder.url, requestBuilder.outputDirOrFile, (IHttpDownloadProgressResultHandler) requestBuilder.httpResultHandler, requestPageFlag, requestBuilder.requestCodeOut);
        }
    }

    /***
     * Loading框相关的配置
     */
    private class LoadingConfig {
        /**
         * 是否自动显示加载进度框
         */
        public boolean autoShowLoading = true;

        /**
         * 是否自动关闭对话框(为了让API调用者看到效果，调试之用。)
         */
        public boolean autoDismissLoading = true;

        /**
         * 加载进度框默认显示的文字 (默认为空)
         */
        public String loadingTipTxt = null;
    }
}
